# -*- coding: UTF-8 -*-
"""
Ce module convertit les URL demandées en appels de fonctions-vues.

RegexURLResolver est la classe principale de ce module. Sa méthode resolve() accepte
une URL en argument (sous forme de chaîne) et renvoie un tuple de la forme suivante :

    (fonction_vue, dico_des_arguments_pour_fonction_vue)
"""

from django.core.exceptions import Http404, ViewDoesNotExist
import re

def get_mod_func(callback):
    # Convertit 'django.views.actus.articles.detail_article' en
    # ['django.views.actus.articles', 'detail_article'].
    dot = callback.rindex('.')
    return callback[:dot], callback[dot+1:]

class RegexURLPattern:
    def __init__(self, regex, callback, default_args=None):
        self.regex = re.compile(regex)
        # callback est une chaîne de la forme 'toto.views.actus.articles.detail_article',
        # représentant donc le chemin d'accès d'une fonction-vue (la dernière partie
        # du chemin est le nom de la fonction-vue, le reste désigne le module où elle
        # se trouve).
        self.callback = callback
        self.default_args = default_args or {}

    def search(self, path):
        match = self.regex.search(path)
        if match:
            args = dict(match.groupdict(), **self.default_args)
            try: # On charge paresseusement self.func.
                return self.func, args
            except AttributeError:
                self.func = self.get_callback()
            return self.func, args

    def get_callback(self):
        mod_name, func_name = get_mod_func(self.callback)
        try:
            return getattr(__import__(mod_name, '', '', ['']), func_name)
        except (ImportError, AttributeError), e:
            raise ViewDoesNotExist, "La vue '%s' est introuvable. L'erreur qui s'est déclenchée est la suivante : %s" % (self.callback, str(e))

class RegexURLMultiplePattern:
    def __init__(self, regex, urlconf_module):
        self.regex = re.compile(regex)
        # urlconf_module est une chaîne représentant un module contenant des associations URL/vue.
        self.urlconf_module = urlconf_module

    def search(self, path):
        match = self.regex.search(path)
        if match:
            new_path = path[match.end():]
            try: # On charge paresseusement self.url_patterns.
                self.url_patterns
            except AttributeError:
                self.url_patterns = self.get_url_patterns()
            for pattern in self.url_patterns:
                sub_match = pattern.search(new_path)
                if sub_match:
                    return sub_match

    def get_url_patterns(self):
        return __import__(self.urlconf_module, '', '', ['']).urlpatterns

class RegexURLResolver:
    def __init__(self, url_patterns):
        # url_patterns est une liste d'objets RegexURLPattern ou RegexURLMultiplePattern.
        self.url_patterns = url_patterns

    def resolve(self, app_path):
        # app_path est le chemin d'accès web demandé, dans sa totalité. La méthode
        # suppose que le chemin d'accès commence par un slash mais elle ne suppose
        # pas que le chemin d'accès se termine par un slash.
        # Exemples :
        #     "/actus/2005/mai/"
        #     "/actus/"
        #     "/sondages/dernier"
        # Une page d'accueil (ou racine) est représentée au moyen de la chaîne "/".
        app_path = app_path[1:] # On enlève le slash du début.
        for pattern in self.url_patterns:
            match = pattern.search(app_path)
            if match:
                return match
        # Aucune des expressions régulières ne correspond, on déclenche une erreur 404
        raise Http404, "Tous les modèles d'URL ont été testés mais aucune correspondance n'a été établie pour l'URL %r" % app_path

class Error404Resolver:
    def __init__(self, callback):
        self.callback = callback

    def resolve(self):
        mod_name, func_name = get_mod_func(self.callback)
        try:
            return getattr(__import__(mod_name, '', '', ['']), func_name), {}
        except (ImportError, AttributeError), e:
            raise ViewDoesNotExist, "La vue '%s' est introuvable. L'erreur qui s'est déclenchée est la suivante : %s" % (self.callback, str(e))
